﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace BUGCOME.Infrastructure.Helper
{
    public class CryptoHelper
    {
        #region AES 常量与核心方法

        // 【固定】默认初始化向量(IV)字符串（与JS的 defaultIv 一致），CBC模式要求IV必须16字节
        private const string DefaultIvString = "flexjobercn12345";

        /// <summary>
        /// 【新增】获取初始化向量(IV)（对齐JS的 getIV 函数逻辑）
        /// </summary>
        /// <param name="uuid">可选UUID（用于动态生成IV）</param>
        /// <returns>符合AES要求的16字节IV字节数组</returns>
        private static byte[] GetIV(string uuid)
        {
            // 1. 判断UUID有效性：非null、非空字符串（C#无undefined，只需判断null和空）
            if (!string.IsNullOrEmpty(uuid))
            {
                // 2. 处理UUID长度：不足16字符补"0"，超过16字符截断（与JS逻辑完全一致）
                string processedUuid = uuid.Length < 16
                    ? uuid.PadRight(16, '0')  // 不足16位补0
                    : uuid.Substring(0, 16);  // 超过16位截断前16位

                // 3. 转为UTF8字节数组（与JS的 CryptoJS.enc.Utf8.parse 对齐）
                return Encoding.UTF8.GetBytes(processedUuid);
            }
            // 4. UUID无效时，使用默认IV
            return Encoding.UTF8.GetBytes(DefaultIvString);
        }

        /// <summary>
        /// AES加密（CBC模式/PKCS7填充，与JS完全对齐）
        /// </summary>
        /// <param name="plainText">待加密明文</param>
        /// <param name="key">加密密钥</param>
        /// <param name="uuid">可选UUID（用于动态生成IV）</param>
        /// <returns>Base64编码的密文</returns>
        /// <exception cref="ArgumentNullException">明文或密钥为空时抛出</exception>
        public static string AesEncrypt(string plainText, string key, string uuid = null)
        {
            // 参数校验（增强健壮性）
            if (string.IsNullOrEmpty(plainText))
                throw new ArgumentNullException(nameof(plainText), "明文不能为null或空");
            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException(nameof(key), "密钥不能为null或空");

            // 1. 获取IV（动态判断UUID）
            byte[] ivBytes = GetIV(uuid);
            // 2. 处理密钥（与JS的 padKey 逻辑一致）
            byte[] keyBytes = PadKey(key);

            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = keyBytes;
                aesAlg.IV = ivBytes;
                aesAlg.Mode = CipherMode.CBC;       // 固定CBC模式（与JS一致）
                aesAlg.Padding = PaddingMode.PKCS7; // 固定PKCS7填充（与JS的 Pkcs7 一致）

                // 创建加密器
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                using (var ms = new MemoryStream())
                using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                using (var sw = new StreamWriter(cs, Encoding.UTF8))
                {
                    // 写入明文并完成加密
                    sw.Write(plainText);
                    sw.Flush();
                    cs.FlushFinalBlock(); // 必须调用：确保最后一块数据正确填充并加密

                    // 密文转为Base64（与JS的 CryptoJS.enc.Base64.stringify 一致）
                    return Convert.ToBase64String(ms.ToArray());
                }
            }
        }

        /// <summary>
        /// AES解密（CBC模式/PKCS7填充，与JS完全对齐）
        /// </summary>
        /// <param name="cipherTextBase64">Base64编码的密文</param>
        /// <param name="key">解密密钥（必须与加密密钥一致）</param>
        /// <param name="uuid">可选UUID（必须与加密时的UUID一致）</param>
        /// <returns>解密后的明文</returns>
        /// <exception cref="ArgumentNullException">密文或密钥为空时抛出</exception>
        public static string AesDecrypt(string cipherTextBase64, string key, string uuid = null)
        {
            // 参数校验（增强健壮性）
            if (string.IsNullOrEmpty(cipherTextBase64))
                throw new ArgumentNullException(nameof(cipherTextBase64), "密文不能为null或空");
            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException(nameof(key), "密钥不能为null或空");

            // 1. 获取IV（动态判断UUID，必须与加密时一致）
            byte[] ivBytes = GetIV(uuid);
            // 2. 处理密钥（与加密时逻辑一致）
            byte[] keyBytes = PadKey(key);
            // 3. 密文Base64解码（与JS的解密输入格式一致）
            byte[] cipherBytes = Convert.FromBase64String(cipherTextBase64);

            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = keyBytes;
                aesAlg.IV = ivBytes;
                aesAlg.Mode = CipherMode.CBC;
                aesAlg.Padding = PaddingMode.PKCS7;

                // 创建解密器
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                using (var ms = new MemoryStream(cipherBytes))
                using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
                using (var sr = new StreamReader(cs, Encoding.UTF8))
                {
                    // 读取解密后的明文
                    return sr.ReadToEnd();
                }
            }
        }

        /// <summary>
        /// 密钥处理（与JS的 padKey 逻辑完全对齐）
        /// AES支持密钥长度：16字节(AES-128)、24字节(AES-192)、32字节(AES-256)
        /// </summary>
        /// <param name="str">原始密钥字符串</param>
        /// <returns>符合AES要求的密钥字节数组</returns>
        public static byte[] PadKey(string str)
        {
            if (string.IsNullOrEmpty(str))
                throw new ArgumentNullException(nameof(str), "原始密钥不能为null或空");

            // 转为UTF8字节数组（与JS的 CryptoJS.enc.Utf8.parse 对齐）
            byte[] keyBytes = Encoding.UTF8.GetBytes(str);
            int keyLength = keyBytes.Length;

            // 按字节长度判断（与JS逻辑完全一致）
            return keyLength switch
            {
                16 or 24 or 32 => keyBytes, // 长度正好符合，直接返回
                < 16 => Encoding.UTF8.GetBytes(str.PadRight(16, '0')), // 不足16字节补0
                < 24 => Encoding.UTF8.GetBytes(str.PadRight(24, '0')), // 16-24字节补0至24字节
                < 32 => Encoding.UTF8.GetBytes(str.PadRight(32, '0')), // 24-32字节补0至32字节
                _ => Encoding.UTF8.GetBytes(str.Substring(0, 32))      // 超过32字节截断前32字节
            };
        }

        #endregion AES 常量与核心方法
    }
}
